﻿using System;
using System.Collections.Generic;
using System.Text;
using Tree.Events;
using System.Runtime.Remoting.Messaging;

namespace Tree.Events.Impl
{
    public class EventsManagerImpl : IEventsManager
    {
        private Dictionary<string, List<EventHandler>> listeners = new Dictionary<string, List<EventHandler>>();

        public void Subscribe(string name, EventHandler handler)
        {
            List<EventHandler> l = null;
            if (!listeners.ContainsKey(name))
            {
                l = new List<EventHandler>();
                listeners.Add(name, l);
            }
            l = listeners[name];
            l.Add(handler);
        }

        private void PublishEvent<T>(string name, object sender, T args, bool async) where T : EventArgs
        {
            if (listeners.ContainsKey(name))
            {
                List<EventHandler> l = listeners[name];
                foreach (EventHandler e in l)
                {
                    if (async)
                    {
                        e.BeginInvoke(sender, args, EndAsyncInvoke, null);
                    }
                    else
                    {
                        e.Invoke(sender, args);
                    }
                }
            }
        }

        private void EndAsyncInvoke(IAsyncResult result)
        {
            AsyncResult ar = (AsyncResult) result;
            EventHandler invokedMethod = (EventHandler)ar.AsyncDelegate;

            try
            {
                invokedMethod.EndInvoke(result);
            }
            catch
            {
            }
        }

        public void AsyncPublish<T>(string name, object sender, T args) where T : EventArgs
        {
            PublishEvent<T>(name, sender, args, true);
        }

        public void Publish<T>(string name, T args) where T : EventArgs
        {
            PublishEvent<T>(name, null, args, false);
        }

        public void Publish<T>(string name, object sender, T args) where T : EventArgs
        {
            PublishEvent<T>(name, sender, args, false);
        }

        public void AsyncPublish<T>(string name, T args) where T : EventArgs
        {
            PublishEvent<T>(name, null, args, true);
        }

        public void AsyncPublish(string name, object sender, object args)
        {
            PublishEvent<WrappedArgument>(name, sender, new WrappedArgument(args), true);
        }

        public void Publish(string name, object sender, object args)
        {
            PublishEvent<WrappedArgument>(name, sender, new WrappedArgument(args), false);
        }

        public void AsyncPublish(string name, object args)
        {
            PublishEvent<WrappedArgument>(name, null, new WrappedArgument(args), true);
        }

        public void Publish(string name, object args)
        {
            PublishEvent<WrappedArgument>(name, null, new WrappedArgument(args), false);
        }
    }
}
